STM32 DAC+TIMER+DMA输出正弦波 您所在的位置:网站首页 STM32输出正弦波+cubeMX配置+HAL库 STM32 DAC+TIMER+DMA输出正弦波

STM32 DAC+TIMER+DMA输出正弦波

2024-06-25 13:36| 来源: 网络整理| 查看: 265

前段时间学习了STM32使用DAC模块输出正弦波的功能,在学习过程中遇到了一些问题,在此和各位分享。 DAC是数字/模拟转换模块的简称,STM32中的DAC是12位数字输入,这个就决定了其精度。STM32的DAC模块具有两个通道,可单独进行转换,也就是说可以同时输出两个正弦波或其他波形。输出正弦波的原理简单讲就是每隔一定时间向DAC的数据寄存器写入数据,然后进行数据转换,输出不同电压,然后在时间轴上显示出波形。 这里比较重要的一个公式就是数字量和模拟量的转换公式:

在这里插入图片描述 STM32芯片内部有个参考电压:VREF,这个电压约为3.3V,由于DAC是12位的,所以最大可以表示为4095,将这3.3V电压均分为4095份,如果你向寄存器内写入2048,那么转换后的电压就是3.3V的一半,如果写入4095,那么转换后的电压就约为3.3V,实际输出可能会略有偏差。 简单介绍了下DAC的原理,下面直接来说我所遇到的问题: 下图就是我当时写好代码输出的正弦波,只有一半的正弦波是对的,另外一半有问题。 在这里插入图片描述 我的设计方式采用的是网上分享比较多的DAC+TIMER+DMA的方式,DMA简单理解就是一个通道,从通道一端写入数据,另外一端直达DAC的数据寄存器。这么做有什么好处呢?不占用CPU资源,速度快速,由于我们输出波形的时候,需要很快频率的DAC转换,如果我们写入过程太慢,产生的波形就会出现锯齿。 下面是我写的代码: 1.首先是GPIO的配置,STM32103的芯片DAC输出管脚对应的是PA4,PA5,配置成模拟输入方式就可以了,这比较简单。

void aw_gpio_init(void) { GPIO_InitTypeDef aw_led_gpio_struct; GPIO_InitTypeDef aw_dac_gpio_struct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC | RCC_APB1Periph_TIM2, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE); aw_led_gpio_struct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; aw_led_gpio_struct.GPIO_Mode = GPIO_Mode_Out_PP; aw_led_gpio_struct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &aw_led_gpio_struct); GPIO_SetBits(GPIOC, GPIO_Pin_10 | GPIO_Pin_12); GPIO_ResetBits(GPIOC, GPIO_Pin_11); // green led on aw_dac_gpio_struct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; aw_dac_gpio_struct.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &aw_dac_gpio_struct); // init PA4 and PA5 for DAC1 }

2.TIMER的配置,在这里我使用的是TIM2,时钟不分频,自动重装载计数值用的是19,这和ST官方的例程中分享的也是一致的。定时器在这里的作用就是定时,每隔一段时间向DMA接口写入数据。

#ifndef __AW_TIME_H__ #define __AW_TIME_H__ #include "stm32f10x_tim.h" #define AW_DAC_PSC (0x0) #define AW_DAC_PERIOD (0x19) void aw_time_init(void); #endif /* __AW_TIME_H__ */ void aw_time_init(void) { TIM_TimeBaseInitTypeDef aw_time_struct; aw_time_struct.TIM_Prescaler = AW_DAC_PSC; aw_time_struct.TIM_CounterMode = TIM_CounterMode_Up; aw_time_struct.TIM_Period = AW_DAC_PERIOD; // timer delay 0.278us once aw_time_struct.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, &aw_time_struct); TIM_Cmd(TIM2, ENABLE); TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); }

3.DAC的配置,在这里我使用了双通道,也就是PA4,PA5管脚都可以输出正弦波。

void aw_dac_init(void) { DAC_InitTypeDef aw_dac_struct; aw_dac_struct.DAC_Trigger = DAC_Trigger_T2_TRGO; // use TIM2 to trigger DAC aw_dac_struct.DAC_WaveGeneration = DAC_WaveGeneration_None; aw_dac_struct.DAC_OutputBuffer = DAC_OutputBuffer_Disable; DAC_Init(DAC_Channel_1, &aw_dac_struct); DAC_Init(DAC_Channel_2, &aw_dac_struct); DAC_Cmd(DAC_Channel_1, ENABLE); // enable DAC_Channel_1 DAC_Cmd(DAC_Channel_2, ENABLE); // enable DAC_Channel_2 DAC_DMACmd(DAC_Channel_2, ENABLE); }

以上部分和各位网友的配置都一样,都没有问题,关键的地方在DMA的配置上。 4.DMA配置 我先给出DMA的配置代码,后续我再给大家阐述:

void aw_dma_init(void) { DMA_InitTypeDef aw_dma_struct; aw_dma_struct.DMA_PeripheralBaseAddr = DAC_DHR12RD_ADDRESS; aw_dma_struct.DMA_MemoryBaseAddr = (AW_U32)&dual_sine_wave_data; aw_dma_struct.DMA_DIR = DMA_DIR_PeripheralDST; aw_dma_struct.DMA_BufferSize = SINE_WAVE_DATA_MAX; aw_dma_struct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; aw_dma_struct.DMA_MemoryInc = DMA_MemoryInc_Enable; aw_dma_struct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; aw_dma_struct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; aw_dma_struct.DMA_Mode = DMA_Mode_Circular; aw_dma_struct.DMA_Priority = DMA_Priority_High; aw_dma_struct.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA2_Channel4, &aw_dma_struct); DMA_Cmd(DMA2_Channel4, ENABLE); }

(1)DAC_DHR12RD_ADDRESS对应的是DAC的数据寄存器的地址,我在.h文件中是这样定义的:#define DAC_DHR12RD_ADDRESS (DAC_BASE + 0x20),其实也可以定义成:#define DAC_DHR12RD_ADDRESS 0x40007420,其实都是一样的,前者是野火中使用的方法,后者是ST官方例程的用法。这里各位和我一样的新手要注意理解。 (2)然后就是DMA的DMA_MemoryBaseAddr这一项的配置,这是数据的输入接口,我使用的是一个数组buff,给这项传递指针。数字量的计算过程如下所示:

#ifndef __AW_DMA_H__ #define __AW_DMA_H__ #include "aw_type.h" #define DAC_DHR12RD_ADDRESS (DAC_BASE + 0x20) #define SINE_WAVE_DATA_MAX (256) #define PI (3.14159) void aw_dma_init(void); void aw_sine_wave_data(AW_U16 cycle); #endif /* __AW_DMA_H__ */ static AW_U16 dual_sine_wave_data[SINE_WAVE_DATA_MAX] = {0}; static AW_U16 sine_wave_data[SINE_WAVE_DATA_MAX] = {0}; /** * @brief This function is used to get sine value. */ void aw_sine_wave_data(AW_U16 cycle) { AW_U16 i = 0; for (i = 0; i dual_sine_wave_data[i] = (sine_wave_data[i] ; } }

主函数很简单,就是配置好各个模块,然后while循环一下。 最后我试验测试的正确波形: 在这里插入图片描述 如有疑问,欢迎各位博友一起讨论!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有